package scene.shape.materials;

import raytracing.HitRecord;
import raytracing.Ray;
import scene.Scene;
import util.Color3f;
import util.Vector3f;

public class RefractionMaterial extends Material {

	private double nt,n = 1.;
	private Color3f admittance;

	public RefractionMaterial(String name, double refractionIndex, Color3f admittance) {
		super(new Color3f(0,0,0), name);
		this.nt = refractionIndex;
		this.admittance = new Color3f(Math.log(admittance.x), Math.log(admittance.y), Math.log(admittance.z));
	}
	
	@Override
	public Color3f shade(HitRecord hr, int recursiondepth){
		if(recursiondepth > MAX_RECURSIONDEPTH)
			return new Color3f(0,0,0);
		Vector3f d = hr.getRay().getDirection(),
				 n = hr.getNormal(),
				 p = hr.getIntersectionPoint();
		double dDotN = d.dot(n);
		Vector3f r = reflect(hr);
		Vector3f t = new Vector3f();
		Color3f k;
		double c;
		if(dDotN < 0){
			refract(d,n,this.n, t);
			c = d.scale(-1).dot(n);
			k = new Color3f(1,1,1);
		}else{
			k = new Color3f(Math.exp(-admittance.x * hr.getT()), Math.exp(-admittance.y * hr.getT()), Math.exp(-admittance.z * hr.getT()));
			if(refract(d, n.scale(-1),1/this.n, t))
				c = t.dot(n);
			else
				return k.multiply(Scene.getInstance().color(new Ray(p, p.add(r)),0 + Scene.EPSILON, Float.POSITIVE_INFINITY, recursiondepth+1));
		}
		double R0 = ((this.n - 1) * (this.n - 1)) / ((this.n + 1) + (this.n + 1));
		double R = R0 + (1 - R0) * Math.pow((1 - c),5);
		return k.multiply(Scene.getInstance().color(new Ray(p,p.add(r)),0 + Scene.EPSILON, Float.POSITIVE_INFINITY, recursiondepth+1).scale((float)R)
				.add(Scene.getInstance().color(new Ray(p, p.add(t)),0 + Scene.EPSILON, Float.POSITIVE_INFINITY, recursiondepth+1).scale(1-(float)R)));
	}

	private boolean refract(Vector3f d, Vector3f n, double n2, Vector3f t) {
		double dDotN = d.dot(n);
		double underRoot = 1 - (n2 * n2 * ((1 - (dDotN * dDotN)) / (nt * nt)));
		if(underRoot >= 0.){
			t.set(d.subtract(n.scale((float)dDotN)).scale((float)(n2 / nt)). //n * (d - n(d.n))/nt
					subtract(n.scale((float)Math.sqrt(underRoot))));	  // - n * sqrt...
			return true;
		}
		return false;
	}

	private Vector3f reflect(HitRecord hr) {
		Vector3f d = hr.getRay().getDirection(),
				 n = hr.getNormal();
		return d.subtract(n.scale(2 * d.dot(n)));
	}
	
	@Override
	public boolean isLightingDependent(){
		return false;
	}
	
	

}
